package apigateway.project.core.filter.flow.flowforInstance;

import apigateway.project.commen.enums.ResponseCode;
import apigateway.project.commen.exception.LimitedException;
import apigateway.project.core.CacheConfig;
import apigateway.project.core.context.GlobalContext;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

import java.util.ArrayList;
import java.util.List;

public class FlowControlByInstanceRule implements GatewayFlowControlRuleForInstance{

    private GlobalContext globalContext;

    private int InstanceNumber;

    private String ModifyHost;

    public FlowControlByInstanceRule(GlobalContext globalContext, int InstanceNumber, String ModifyHost) {
        this.globalContext = globalContext;
        this.InstanceNumber = InstanceNumber;
        this.ModifyHost = ModifyHost;
    }

    public FlowControlByInstanceRule(GlobalContext globalContext, int InstanceNumber) {
        this.globalContext = globalContext;
        this.InstanceNumber = InstanceNumber;
    }

    public FlowControlByInstanceRule(GlobalContext globalContext, String ModifyHost) {
        this.globalContext = globalContext;
        this.ModifyHost = ModifyHost;
    }

    public FlowControlByInstanceRule(GlobalContext globalContext) {
        this.globalContext = globalContext;
    }

    @Override
    public void doFilterControlFilter() {
        initFlowRule();
        Entry entry = null;
        try{
            entry = SphU.entry(ModifyHost);
            //如果有需要，可以进行一些其他的处理
        }catch (BlockException e){
            throw new LimitedException(ResponseCode.FLOW_CONTROL_ERROR);
        }finally {
            if(entry != null){
                entry.exit();
            }
        }
    }

    /**
     * 动态更新针对具体实例的规则，类似于懒加载，当有具体的实例被负载均衡挑选出来的时候才对其资源进行流控
     */
    void initFlowRule() {
        //多线程环境下会有并发修改问题,由于CacheConfig是单例的，所以把它作为对象锁
        synchronized (CacheConfig.getCacheConfig()) {
            List<FlowRule> flowRules = FlowRuleManager.getRules();
            if (flowRules == null) {
                flowRules = new ArrayList<>();
            }
            if (FlowRuleManager.hasConfig(ModifyHost)) {
                return;
            }
            FlowRule flowRule = new FlowRule();
            flowRule.setResource(ModifyHost);
            flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            flowRule.setCount(globalContext.getRule().getFlowConfig().getCount() / InstanceNumber);
            flowRules.add(flowRule);
            FlowRuleManager.loadRules(flowRules);
        }
    }
}
